#ifndef ACTOR_HPP__
#define ACTOR_HPP__

/**
 * \file actor.hpp
 * \author Mason Smith <masonium@caltech.edu>
 * This file contains the Actor class, which holds
 * the instruction set and global state of a BMD program, 
 * as well as the virtual machine which runs the program.
 */

#include <string>
#include <vector>
#include <map>
#include <iostream>
#include <boost/smart_ptr.hpp>
#include "instr.hpp"

using std::string;
using std::vector;
using std::map;
using std::multimap;
using std::ostream;

using boost::shared_ptr;
using boost::weak_ptr;

namespace BMD
{
  /**** forward declarations ****/
  class VM;
  class assert_test;

  /** structure that contains information about
      each function */
  struct function_info
  {
    function_info();
    function_info(int _addr, uint _num_args);

    int addr;
    uint num_args;
  };

  /**** typedefs ****/
  typedef map<string, int> LabelMap;  
  typedef map<string, function_info> FunctionMap;
  typedef multimap<int, shared_ptr<assert_test> > AssertionMap;

  typedef unsigned int uint;

  /**
   * An actor is an independent agent with machine code and global state, 
   * as well as a virtual machine that it runs on.
   */
  class Actor
  {
  public:
    Actor(unsigned int num_fields = 0);
    Actor(string filename, unsigned int num_fields = 0);

    //! Loads the instructions from a file 
    void load_from_file(string filename);

    //! Returns the address associated with a label
    int get_label_address(string label, bool use_functions = true);

    //! returns the function with the given name
    function_info get_function(string name);

    //! grab the instruction at a particular address
    InstructionRef get_instruction(int addr);
    int get_num_instructions() const { return instructions.size(); }
      
    //! check the validity of the instruction pointer
    bool is_valid_ip(int ip) const 
    { return (ip < (int)instructions.size()) and (ip >= 0); }

    /**** testing only ****/
    void add_instruction(InstructionRef in);
    void add_instruction(Instruction* in) 
    { add_instruction(InstructionRef(in)); }
    void add_label(string& str, int addr);

    bool should_throw_error() const { return should_throw; }

    /*** END testing only ****/

    //! checks the assertions at the current ip, throwing errors if any fail
    void validate_assertions();

    /* field accessors */
    Value get_field(int i) const { return fields.at(i); }
    void set_field(int i, Value f) { fields[i] = f; }
    void alloc_fields(unsigned int num) { fields.resize(num); }

    void set_linked_actor(shared_ptr<Actor> i) { linked_actor = weak_ptr<Actor>(i); }
    shared_ptr<Actor> get_linked_actor() const { return linked_actor.lock(); }

    /**** VM-linked operations ****/
    void set_debug_output(bool debug);
    void run();
    void run_with_assertions();
    bool is_running() const;
    Value call_function(string func_name, const vector<Value>& args, bool debug = false);
    VM* get_vm() const { return vm; }
    
    /** Flags the VM as currently running, but does *not* start any
        instructions. */
    void start();
    
    ~Actor();

    static const int NO_LABEL;

  private:

    //! remove all instructions and other data 
    void clear();

    bool is_comment(string line); //< returns true if a line is a comment
    bool is_label(string line); //< returns true if a line is a label
    bool is_execution_signal(string line);

    // parse an execution signal
    void parse_execution_signal(string line, int op_num);

    //! Parse and create a single instruction
    Instruction* parse_instruction(string instr);
    
    //! parse a nonnegative integer
    unsigned int parse_nonnegative_integer(string arg,
                                           string name);
    
    //! Parse a numeric value
    Value parse_numeric_value(string s);

    //! Parse a generic argument
    Value parse_value(string s);

    // list of instructions for the actor
    vector<InstructionRef> instructions;
    
    // label map
    LabelMap label_map;

    // function map
    FunctionMap function_map;

    // name of the initialization function
    string init_func;

    // global fields
    vector<Value> fields;

    // assertions
    AssertionMap assertions;

    // actor link
    weak_ptr<Actor> linked_actor;

    // indicates whether the actor should throw
    bool should_throw;

    // underlying virtual machine
    VM* vm;
  };

  typedef shared_ptr<Actor> ActorRef;
}


#endif
